home *** CD-ROM | disk | FTP | other *** search
- /*
- * Cron.cmm
- *
- * This script is designed to be left running all the time. At times specified
- * in the crontab file, other scripts and programs are run.
- *
- * CRONTAB format is each line as such:
- * MINUTE HOUR DAY_OF_MONTH MONTH DAY_OF_WEEK Command
- *
- * All fields are 0-based, except day of month. So the possible values are 0 to
- * 1 less than you would expect. Sunday is 0 as is January.
- *
- * You can actually specify numbers in the field, or put in a * to match
- * anything. You can use Words for month or day, use the first three letters.
- * You can use numeric ranges or lists of numbers, such as:
- *
- * 1-4,5,6,8-10
- *
- *
- * Ranges (and * which is consider min-max) can have a step, such as
- * "* / 5" which means every five units. Spaces are not allowed.
- *
- * The day of month and day of week will both set off a match, so the entry
- * will be run if EITHER matches. Use a dash in either field to match nothing.
- *
- *
- * This version is designed to work on any CEnvi platform. If you come across
- * something that does not work, please report it to us.
- *
- * Also note that this program uses some of the .CMM scripts distributed with
- * CEnvi. If it cannot find them, some file types will not work. Currently
- * supported file types and systems are:
- *
- *
- * .NCF, .NLM Netware
- * .BAT All (Netware uses a batch interpreter script)
- * .CMD OS/2
- * .COM,.EXE All except Netware
- * .CMM All
- *
- * Any other extension will be done in the most generic way for that system.
- * Hopefully, it will work.
- *
- * If you preceed the particular command with a '=', it is done synchronously
- * as appropriate, usually on the same screen as cron. Don't let such
- * applications hang or ask for input, because it is unlikely anyone will
- * be around to do it. In this case, cron will be stuck waiting.
- *
- * The default is to launch the job asynchronously.
- */
-
- // The cron.tab's default location
- if( defined(_NWNLM_) )
- {
- crontab = "sys:/cron.tab";
- } else {
- crontab = "c:\\cron.tab";
- }
-
- seconds = 1;
-
- // The last datestamp of the cron.tab file
- last_time = 0;
-
- /* ---------------------------------------------------------------------- */
-
- /* Here is a C definition of the structure we are using, for easy of
- * modification
- *
- * struct cron_entry {
- * char *command;
- * BYTE minute[60]; // We use flags for each of these fields
- * BYTE hour[24];
- * BYTE day[31];
- * BYTE month[12];
- * BYTE weekday[7];
- * };
- */
-
- /* ---------------------------------------------------------------------- */
-
- /*
- * Process a single numeric field, filling in the entries of the array
- * to match. Return 0 on success, 1 on failure. Eat up all white space
- * after the field.
- */
- process_field(line,entry_array,max,text_strings,offset)
- {
- // By default, nothing is turned on.
- for( i=0;i<max;i++ ) entry_array[i] = 0;
-
- comma_ok = 0;
- while( line[0] && !isspace(line[0]) )
- {
- if( comma_ok==1 && line[0]==',' ) line++;
- comma_ok = 1;
-
- if( line[0]=='-' ) { line++; continue; }
- if( isdigit(line[0]) || line[0]=='*' )
- {
- first = 0; end = max-1; step = 1;
-
- // First get a possible range
- if( isdigit(line[0]) )
- {
- while( isdigit(line[0]) )
- { first = 10*first + line[0]-'0'; line++; }
-
- if( line[0]=='-' )
- {
- line++;
- end = 0;
- while( isdigit(line[0]) )
- { end = 10*end + line[0]-'0'; line++; }
- } else end = first;
- }
- else line++;
-
- if( end<offset || end>=max+offset )
- {
- printf("Value is out of the valid range of %d-%d.\n",offset,max-1+offset);
- return 1;
- }
-
- // And there may be a step value
- if( line[0]=='/' )
- {
- line++;
- step = 0;
- while( isdigit(line[0]) )
- { step = 10*step + line[0]-'0'; line++; }
- }
-
- // Finally, set all those entries on.
- for( i=first;i<=end;i+=step ) entry_array[i-offset] = 1;
- } else {
- if( text_strings==NULL || !isalpha(line[0]) ) return 1;
- for( i=0;i<=GetArraySpan(text_strings);i++ )
- if( !strnicmp(text_strings[i],line,3) )
- {
- entry_array[i-offset] = 1;
-
- line+=3; break;
- }
- if( i>GetArraySpan(text_strings) )
- {
- printf("Unrecognized text name for this field.\n");
- return 1;
- }
- }
- }
-
- while( isspace(line[0]) ) line++;
-
- return 0;
- }
-
-
- months =
- { "jan","feb","mar","apr","may","jun","jul","aug","sep","oct","nov","dec" };
- days =
- { "sun","mon","tue","wed","thu","fri","sat" };
-
- /*
- * Process a single line from the cron.tab file and return an entry structure
- * describing it. If the line is blank, a comment, or illegal return NULL;
- */
- process_cron_line(line,line_number)
- {
- while( isspace(line[0]) ) line++;
-
- // Ignore comments or blank lines.
- if( line[0]=='\n' || line[0]=='\0' || line[0]=='#' ) return NULL;
-
- // The line's format is described in the comments at the top of this file.
- if( process_field(line,entry.minute,60,NULL,0) )
- { printf("Illegal entry field 1 on line %d.\n",line_number); return NULL; }
- if( process_field(line,entry.hour,24,NULL,0) )
- { printf("Illegal entry field 2 on line %d.\n",line_number); return NULL; }
- if( process_field(line,entry.day,32,NULL,1) )
- { printf("Illegal entry field 3 on line %d.\n",line_number); return NULL; }
- if( process_field(line,entry.month,12,months,0) )
- { printf("Illegal entry field 4 on line %d.\n",line_number); return NULL; }
- if( process_field(line,entry.weekday,7,days,0) )
- { printf("Illegal entry field 5 on line %d.\n",line_number); return NULL; }
-
- strcpy(entry.command,line);
- s = strlen(entry.command) - 1;
- if( entry.command[s]=='\n' ) entry.command[s] = '\0';
-
- return entry;
- }
-
- /* ---------------------------------------------------------------------- */
-
- print_at_then_return(col,row,text)
- {
- pos = ScreenCursor();
- ScreenCursor(col,row);
- printf("%s",text); fflush(stdout);
- ScreenCursor(pos.col,pos.row);
- }
-
- /* ---------------------------------------------------------------------- */
-
- /*
- * Read the cron.tab file in, creating a cron_entry structure. This structure
- * is returned. Return NULL if unable to read in the cron.tab or if the
- * cron.tab is empty.
- */
- read_crontab()
- {
- sprintf(buffer,"Reading CRONTAB...",timebuf);
- print_at_then_return(60,0,buffer);
- Undefine(entries); entry_count = 0; line_number = 0;
- buffer = ""; SetArraySpan(buffer,512);
-
- if( (fp = fopen(crontab,"r"))==NULL )
- {
- printf("Could not open file \"%s\".\n",crontab);
- return NULL;
- }
- while( !feof(fp) )
- {
- line_number++;
- if( fgets(buffer,512,fp)==NULL ) break;
- entry = process_cron_line(buffer,line_number);
- if( entry!=NULL ) entries[entry_count++] = entry;
- }
- fclose(fp);
-
- file = Directory(crontab);
- if( file==NULL )
- {
- printf("Unable to get timestamp from crontab file.\n");
- return NULL;
- }
- last_time = file[0].Write;
-
- return defined(entries)?entries:NULL;
- }
-
-
- /*
- * The particular entry should be executed now using the system-dependant
- * way of executing them. The file's type is checked and used.
- */
- cron_execute(command)
- {
- exec = "";
- // For cool printing of date-time.
- timebuf = ""; SetArraySpan(timebuf,256);
- strftime(timebuf,256,"%m/%d/%y %H:%M:%S",localtime(time()));
-
-
- sprintf(buffer2,"LAST CRON: [%s] %s",timebuf,command);
- sprintf(buffer,"%-80s",buffer2);
- print_at_then_return(0,2,buffer);
-
- sync = 0;
- if( command[0]=='=' ) { command++; sync = 1; }
-
- if( (end = strchr(command,' '))==NULL )
- end = command + strlen(command) - 4;
- else
- end -= 4;
-
- //
- // Each type of executable that we know about is done for every system as
- // well as we can.
- //
- if( defined(_NWNLM_) && !strnicmp(end,".NCF",4) )
- {
- system(command);
- return;
- }
-
- if( defined(_NWNLM_) && !strnicmp(end,".NLM",4) )
- {
- spawn(P_NOWAIT,command);
- return;
- }
-
- if( !strnicmp(end,".CMM",4) )
- {
- if( sync )
- {
- Interpret(command,INTERP_FILE|INTERP_NOINHERIT_LOCAL|
- INTERP_NOINHERIT_GLOBAL);
- return;
- }
- if( defined(_NWNLM_) )
- {
- sprintf(exec,"load cenvi %s",command);
- system(exec);
- return;
- }
- if( defined(_WINDOWS_) )
- {
- sprintf(exec,"cenviw %s",command);
- spawn(P_NOWAIT,exec);
- return;
- }
- if( defined(_DOS_) )
- {
- sprintf(exec,"cenvid %s",command);
- spawn(P_SWAP,exec);
- return;
- }
- if( defined(_DOS32_) )
- {
- sprintf(exec,"cenvid32 %s",command);
- spawn(P_NOWAIT,exec);
- return;
- }
- if( defined(_OS2_) )
- {
- sprintf(exec,"cenvi2 %s",command);
- spawn(P_NOWAIT,exec);
- return;
- }
- if( defined(_NTCON_) )
- {
- sprintf(exec,"cenvint %s",command);
- spawn(P_NOWAIT,exec);
- return;
- }
- if( defined(_NTWIN_) )
- {
- sprintf(exec,"cenviwnt %s",command);
- spawn(P_NOWAIT,exec);
- return;
- }
-
- // Hmmm, an unrecognized version of CEnvi. Well, this is the most generic
- // way I can launch such a script.
- sprintf(exec,"cenvi %s",command);
- spawn(P_NOWAIT,exec);
- return;
- }
-
- if( !defined(_NWNLM_) && (!strnicmp(end,".EXE",4) || !strnicmp(end,".COM",4)) )
- {
- spawn(sync?P_WAIT:P_NOWAIT,command);
- return;
- }
-
- if( defined(_OS2_) && !strnicmp(end,".CMD",4) )
- {
- spawn(sync?P_WAIT:P_NOWAIT,command);
- return;
- }
-
- if( !strnicmp(end,".BAT",4) )
- {
- if( defined(_NWNLM_) )
- {
- if( sync )
- {
- sprintf(exec,"batch %s",command);
- Interpret(exec,INTERP_FILE|INTERP_NOINHERIT_LOCAL|
- INTERP_NOINHERIT_GLOBAL);
- } else {
- sprintf(exec,"load cenvi batch %s",command);
- spawn(P_NOWAIT,exec);
- }
- return;
- }
- if( defined(_DOS_) )
- spawn(sync?P_WAIT:P_SWAP,command);
- else
- spawn(sync?P_WAIT:P_NOWAIT,command);
- return;
- }
-
- if( defined(_NWNLM_) )
- {
- spawn(P_NOWAIT,command);
- return;
- }
-
-
- if ( !strnicmp("start ",command,6) )
- sprintf(exec,"%s",command);
- else
- sprintf(exec,"start %s",command);
- system(exec);
- }
-
-
- /*
- * Go through each entry and see if it should be executed at this time.
- */
- cron_checks(entries)
- {
- current_time = localtime(time());
- doit = 0;
-
- for( i=0;entries && i<=GetArraySpan(entries);i++ )
- {
- if( entries[i].minute[current_time.tm_min] &&
- entries[i].hour[current_time.tm_hour] &&
- entries[i].month[current_time.tm_mon] &&
- // Remember, the two day fields both can match
- (entries[i].day[current_time.tm_mday-1] ||
- entries[i].weekday[current_time.tm_wday] )
- )
- // Needed to stop CEnvi from passing the variable by reference.
- {
- cron_execute(=entries[i].command);
- doit = 1;
- }
- }
-
- if( doit) update_list(entries);
- }
-
-
- /*
- * Check if the crontab file has been changed
- */
- cron_changed()
- {
- file = Directory(crontab);
- if( file==NULL ) return 0;
-
- return last_time!=file[0].Write;
- }
-
-
- /*
- * Keeps a running clock on the screen.
- */
- update_display()
- {
- timebuf = ""; SetArraySpan(timebuf,256);
- strftime(timebuf,256,"%m/%d/%y %H:%M:%S",localtime(time()));
-
-
- sprintf(buffer,"%18s",timebuf);
- print_at_then_return(60,0,buffer);
- }
-
- /* ---------------------------------------------------------------------- */
-
- num_days = { 31,28,31,30,31,30,31,31,30,31,30,31 };
-
-
- /*
- * Given an entry, figure out the next time it will be run. Return a structure
- * describing that time (something to be printed). Return NULL if it
- * will never again be run.
- */
- next_time(entry)
- {
- current_time = localtime(time());
-
-
- // first, determine the next month it is going to happen. It could either
- // be this month from now to the end of the month, or one of the next months
- // any time during the month. If we wrap around months, we must also increment
- // the year.
-
- // Note, 0 and 13 are both this month. However, 0 is limited by this day & time
- // forward only.
- for( month=0;month<13;month++ )
- {
- try_month = (current_time.tm_mon+month)%12;
- // We wrapped into next year.
- if( month+current_time.tm_mon==12 ) current_time.tm_year++;
- // First, if cannot execute this month, we continue;
- if( entry.month[try_month]==0 ) continue;
-
-
- // We find the next day that matched. Note, if the month==0, we do no wrapping
- start_day = (month==0)?(current_time.tm_mday-1):0;
- // We ignore leap years, tough.
- for( day=start_day;day<num_days[try_month];day++ )
- {
- tmp.tm_sec = 0; tmp.tm_min = 0; tmp.tm_hour = 0;
- tmp.tm_mon = try_month; tmp.tm_mday = day+1;
- // rough approximation of DST
- tmp.tm_isdst = (try_month>2 && try_month<10)
- tmp.tm_year = current_time.tm_year;
-
- new = localtime(mktime(tmp));
-
- if( (entry.day[day]==0) && (entry.weekday[new.tm_wday]==0) )
- continue;
-
- // Ok, this month and day is the next possible choice. Let's find a time that will
- // work. Again, if month==0, it must be later than now.
- start_hour = 0;
- if( month==0 && day==start_day ) start_hour = current_time.tm_hour;
- for( hour=start_hour;hour<24;hour++ )
- {
- if( entry.hour[hour]==0 ) continue;
-
- // Finally, the minute: there must be some better way to do all this...
- start_min = 0;
- // Start one minute after now - the 'now' minute has already been done.
- if( month==0 && day==start_day && hour==start_hour )
- start_min = current_time.tm_min+1;
- for( minute = start_min;minute<60;minute++ )
- {
- if( entry.minute[minute] )
- {
- // FOUND IT!!!!
- current_time.tm_mon = try_month;
- current_time.tm_mday = day+1;
- current_time.tm_hour = hour;
- current_time.tm_min = minute;
- return current_time;
- }
- }
- }
- }
- }
-
-
- // We couldn't find a match, return never execute again. With the entry format
- // I believe this really means it can never execute period.
- return NULL;
- }
-
-
-
- datesort(elem1,elem2)
- {
- if( elem1.tm_year<elem2.tm_year ) return -1;
- if( elem1.tm_year>elem2.tm_year ) return 1;
- if( elem1.tm_mon<elem2.tm_mon ) return -1;
- if( elem1.tm_mon>elem2.tm_mon ) return 1;
- if( elem1.tm_mday<elem2.tm_mday ) return -1;
- if( elem1.tm_mday>elem2.tm_mday ) return 1;
- if( elem1.tm_hour<elem2.tm_hour ) return -1;
- if( elem1.tm_hour>elem2.tm_hour ) return 1;
- if( elem1.tm_min<elem2.tm_min ) return -1;
- if( elem1.tm_min>elem2.tm_min ) return 1;
- return 0;
- }
-
-
-
- /*
- * Keep a list of all upcoming events on the screen
- */
- update_list(entries)
- {
- ScreenCursor(1,7); printf("Upcoming Events:\n");
-
- j = 0;
-
- // First we build a table of the next time these entries will be going off
- // along with a text representation of such.
- for( i=0;entries && i<=GetArraySpan(entries);i++ )
- {
- ret = next_time(entries[i]);
- if( ret!=NULL )
- {
- // rough approximation of DST
- ret.tm_isdst = (ret.tm_mon>2 && ret.tm_mon<10)
- next[j] = ret = localtime(mktime(ret));
- next[j++].command = entries[i].command;
- }
- }
- qsort(next,"datesort");
-
- for( i=0;i<10;i++ )
- {
- ScreenCursor(0,9+i);
- if( i<j )
- {
- strftime(buf,"%a, %b %d, %Y at %I:%M %p ",next[i]);
- strcat(buf,next[i].command);
- printf("%-79s\n",buf);
- } else {
- printf(" \n");
- }
- }
-
-
- ScreenCursor(0,19);
- printf("-------------------------------------------------------------------------------\n");
- }
-
- /* ---------------------------------------------------------------------- */
-
- /*
- * Edit the crontab file using an appropriate editor for the system.
- */
- invoke_editor()
- {
- the_editor = "notepad";
- if( defined(_DOS_) || defined(_DOS32_) )
- {
- mode = P_SWAP; the_editor = "edit";
- } else
- mode = P_NOWAIT;
-
- if( defined(_OS2_) ) the_editor = "e";
- if( defined(_NWNLM_) ) the_editor = "edit";
- if( defined(EDITOR) ) the_editor = EDITOR;
- spawn(mode,the_editor,crontab);
- }
-
- /* ---------------------------------------------------------------------- */
-
- main(argc,argv)
- {
- ScreenClear();
- printf("CMM Cron, Version 1.1.\n");
- printf(" [Q] Quit [E] Edit Crontab\n\n");
-
-
- // You can specify the crontab as the first argument.
- if( argc>=2 ) crontab = argv[1];
-
-
- Undefine(entries);
- entries = read_crontab();
-
- printf("-------------------------------------------------------------------------------\n");
- printf("Events will be processed as necessary. You can go away now. To update the\n");
- printf("CRON.TAB at any time, simply edit and save it. Cron will automatically load it.\n");
- printf("-------------------------------------------------------------------------------\n");
- update_list(entries);
-
-
- while( 1 )
- {
- // Get current time, determine how many seconds until the next minute.
- // Use 61 instead of 60 to make sure we have flipped over to the next
- // minute.
- counter = 61 - localtime(time()).tm_sec;
-
- // Wait one minute
- while( counter>0 )
- {
- if( kbhit() )
- switch( toupper(getch()) )
- {
- case 'Q': exit(0);
- case 'E': invoke_editor(); break;
- }
- update_display(); // Prints a timer.
- Suspend(seconds * 1000); // wait some seconds.
- counter -= seconds; // Note that we waited.
- }
-
- if( cron_changed() )
- {
- printf("Crontab file has changed.\n");
- Undefine(entries);
- entries = read_crontab();
- update_list(entries);
- }
-
- cron_checks(entries);
- }
- }
-